# -*- coding: gbk -*-
import copy
import json
import os
import re
from hashlib import md5

PN = 'scns'
PUN = PN.upper()
PCN = PN.capitalize()
state_target = f'//@{PN}State:'
similar_target = f'//@{PN}Similar:'
video_target = f'//@{PN}Video:'
reference_target = f'//@{PN}Reference:'
name_target = f'//@{PN}Name:'


def get_bsp_dirs():
    l = []
    for path, dir_list, file_list in os.walk(rf'../{PN}Bsp'):
        for dir_name in dir_list:
            path_name = os.path.join(path, dir_name, PCN + 'BspConfig.h')
            if os.path.exists(path_name):
                l.append(os.path.join(path, dir_name))
    return l


def from_camel(s):
    lst = []
    for index, char in enumerate(s):
        if char.isupper() and index != 0:
            lst.append("_")
        lst.append(char)
    return "".join(lst).upper()


def to_upper_camel(s):
    return ''.join([word.capitalize() for word in s.split('_')])


def to_lower_camel(s):
    l = [word.capitalize() for word in s.split('_')]
    l[0] = l[0].lower()
    return ''.join(l)


def to_bsp(s):
    return s.replace(PUN, PUN + '_BSP')


def to_bsp_upper_camel(s):
    return to_upper_camel(to_bsp(s))


def to_bsp_lower_camel(s):
    return to_lower_camel(to_bsp(s))


def sort_modules(modules):
    return {x[0]: x[1] for x in sorted(modules.items(), key=lambda x: x[0])}


def get_repository_abs_dir():
    dirr = '../'
    while not os.path.exists(dirr + '.git'):
        dirr += '../'
    return os.path.abspath(dirr)


def get_repository_url():
    origin_url = os.popen('cd ../ && git remote get-url origin').read().strip()
    if origin_url.endswith('.git'):
        origin_url = origin_url[:-4]
    if not origin_url.endswith('/'):
        origin_url = origin_url + '/'
    return origin_url


def replace(file, flag_start, flag_end, code, insert=True):
    if isinstance(flag_start, str) and isinstance(flag_end, str):
        flag_start = [flag_start]
        flag_end = [flag_end]
        code = [code]
    if len(code) != len(flag_start) or len(code) != len(flag_end):
        raise RuntimeError
    lines = open(file, errors='ignore').readlines()
    for b, e, c in zip(flag_start, flag_end, code):
        special = False
        has = False
        result = []
        for l in lines:
            if l.strip() == b:
                special = True
                has = True
                result.append(l)
                result.append(c)
            if l.strip() == e:
                special = False
            if not special:
                result.append(l)
        if not has and insert:
            result += b + f'\n{c}\n{e}\n'
        lines = result
    if not is_same_str(open(file, errors='ignore').read(), ''.join(lines)):
        with open(file, 'w', errors='ignore') as f:
            f.writelines(lines)


def join_line(lines, basic_sp=0, strip=True):
    tl, sp = [], ' ' * basic_sp
    for l in lines:
        if strip:
            l = l.strip()
        if ('}' in l and '{' not in l) or (']' in l and '[' not in l): sp = sp[:-4]
        tl.append(('' if l.startswith('#') else sp) + l + '\n')
        if ('{' in l and '}' not in l) or ('[' in l and ']' not in l): sp = (sp + ' ' * 4)
    return ''.join(tl)


def remove_redundant_parentheses(s: str):
    stack = []
    mapp = {}
    for i, c in enumerate(s):
        if c == '(':
            stack.append(i)
        elif c == ')':
            j = stack.pop()
            mapp[i] = j
    s = list(s)
    for i, j in mapp.items():
        if mapp.get(i + 1) == j - 1:
            s[i] = s[j] = ''
    return ''.join(s)


def split_line(line):
    if type(line) != str:
        ans = []
        for a in line:
            ans += strip_array(split_line(a))
    else:
        ans = ['']
        for c in line:
            if c == '{':
                ans.append('')
            ans[-1] += c
            if c == '{' or c == ';':
                ans.append('')
    return strip_array(ans)


def get_md5(string: str | bytes) -> str:
    mdfive = md5()
    mdfive.update(string)
    return mdfive.hexdigest().upper()


def strip_array(array: list, strip=False) -> list:
    if strip:
        while len(array) != 0 and len(array[-1].strip()) == 0:
            array.pop()
    else:
        while len(array) != 0 and len(array[-1]) == 0:
            array.pop()
    return array


def is_same_str(a, b) -> bool:
    if not isinstance(a, str):
        a = ''.join(a)
    if not isinstance(b, str):
        b = ''.join(b)
    return a.replace('\n', '').replace('\r', '').replace('\t', '').replace(' ', '') == b.replace('\n', '').replace('\r', '').replace('\t', '').replace(' ', '')


def get_c_file_set() -> list:
    modules = sort_modules(json.loads(open(f'script/{PN}Modules.json', 'r', encoding='gbk', errors='ignore').read().encode("utf-8")))
    file_set = []
    for module_name in modules.keys():
        file_set.append(to_upper_camel(module_name) + '.c')
        file_set.append(to_upper_camel(module_name) + '.h')
        file_set.append(to_upper_camel(module_name) + '.table.c')
        file_set.append(to_upper_camel(module_name) + '.table.h')
        file_set.append(to_upper_camel(module_name) + '.color.h')
        file_set.append(to_upper_camel(module_name) + '.reg.h')
        if modules[module_name].get('bsp_status') != 'no':
            file_set.append(to_upper_camel(module_name) + '.c.h')
            file_set.append(to_bsp_upper_camel(module_name) + '.c.h')
        if modules[module_name].get('bsp_status') == 'yes':
            file_set.append(to_bsp_upper_camel(module_name) + '.c')
            file_set.append(to_bsp_upper_camel(module_name) + '.image.c')
            file_set.append(to_bsp_upper_camel(module_name) + '.table.c')
            file_set.append(to_bsp_upper_camel(module_name) + '.table.h')
            file_set.append(to_bsp_upper_camel(module_name) + '.h')
        file_set.append(to_upper_camel(module_name) + '.py')
        file_set.append(to_upper_camel(module_name) + '.md')
    return file_set


types_list = {
    'bool': {'bit': 1, 'structType': 'uint8', 'print': '%hhd', 'scanfType': 'int'},
    'uint8': {'bit': 8, 'print': '%hhi', 'scanfType': 'int'},
    'int8': {'bit': 8, 'print': '%d', 'scanfType': 'int'},
    'uint16': {'bit': 16, 'print': '%d', 'scanfType': 'int'},
    'int16': {'bit': 16, 'print': '%d', 'scanfType': 'int'},
    'uint24': {'bit': 24, 'structType': 'int32', 'print': '%d', 'scanfType': 'int'},
    'uint32': {'bit': 32, 'print': '%d', 'scanfType': 'int'},
    'int32': {'bit': 32, 'print': '%d'},
    'int': {'bit': 32, 'print': '%d'},
    'float': {'bit': 32, 'print': '%f'},
    'uint64': {'bit': 64, 'print': '%llu'},
    'ScnsGyroResult': {
        'values': {
            "lastUs": {"type": "uint64", },
            "temp": {"type": "float", },
            "oAx": {"type": "float", },
            "oAy": {"type": "float", },
            "oAz": {"type": "float", },
            "oDPitch": {"type": "float", },
            "oDRoll": {"type": "float", },
            "oDYaw": {"type": "float", },
            "oMx": {"type": "float", },
            "oMy": {"type": "float", },
            "oMz": {"type": "float", },
            "q": {"type": "float[4][1]", },
            "pitch": {"type": "float", },
            "roll": {"type": "float", },
            "yaw": {"type": "float", },
            "dPitch": {"type": "float", },
            "dRoll": {"type": "float", },
            "dYaw": {"type": "float", },
            "ax": {"type": "float", },
            "ay": {"type": "float", },
            "az": {"type": "float", },
            "pAx": {"type": "float", },
            "pAy": {"type": "float", },
            "pAz": {"type": "float", },
        }
    },
    'ScnsVl53l0xResult': {
        'values': {
            "lastUs": {"type": "uint64", },
            "rangeStatus": {"type": "uint8", },
            "millimeters": {"type": "uint16", },
            "oriMillimeters": {"type": "uint16", },
            "ambientCount": {"type": "uint16", },
            "signalCount": {"type": "uint16", },
        }
    },
    'ScnsImage': {
        'values': {
            "width": {"type": "uint16", },
            "height": {"type": "uint16", },
        },
        'ex_size': '{name}.width*{name}.height',
        'ex_ptr': '{name}.p',
        'ex_init': ['{name}.dynamic=0;', '{name}.format=SCNS_COLOR_FORMAT_GRAY8;'],
        'ex_save': 'scnsBmpWrite(fopen({filename},"wb"),{val},1);',
        'ex_read': '{val}=scnsBmpRead(fopen({filename},"rb"),1,SCNS_COLOR_FORMAT_GRAY8);',
    },
}


def types_list_update(conf=None):
    if conf is not None:
        for k, v in conf.items():
            types_list[k] = v
    for k, v in types_list.items():
        if v.get('structType') is None:
            v['structType'] = k
        if v.get('values') is None:
            if v.get('scanfType') is None:
                v['scanfType'] = k
            types_list[k] = v


types_list_update()


def generate_index_list(array_str: str) -> (list, list):
    match = [int(a) for a in re.findall(r'\[(\d+)]', array_str)]
    ans = []

    def generate(tmp):
        if len(tmp) == len(match):
            ans.append((''.join([f'[{i}]' for i in tmp]), ','.join([f'{i}' for i in tmp]),))
        else:
            for i in range(0, match[len(tmp)]):
                generate(copy.deepcopy(tmp) + [i])

    generate([])
    return ans
